<?php
/**
 *+------------------
 * madong
 *+------------------
 * Copyright (c) https://gitee.com/motion-code  All rights reserved.
 *+------------------
 * Author: Mr. April (405784684@qq.com)
 *+------------------
 * Official Website: http://www.madong.tech
 */

namespace madong\think\wf\dao;

use madong\ingenious\interface\model\IProcessTaskHistory;
use madong\ingenious\libs\utils\ArrayHelper;
use madong\ingenious\libs\utils\PropertyCopier;
use madong\ingenious\enums\ProcessTaskStateEnum;
use madong\think\wf\basic\BaseDao;
use madong\think\wf\model\ProcessTaskHistory;
use madong\think\wf\model\ProcessTaskActorHistory;
use madong\think\wf\model\ProcessInstanceHistory;
use madong\think\wf\model\ProcessDefine;
use madong\think\wf\common\Util;
use think\facade\Db;

class ProcessTaskHistoryDao extends BaseDao
{

    protected function setModel(): string
    {
        return ProcessTaskHistory::class;
    }

    /**
     * 任务详情-think-orm 不支持whereHas 所有只能通过连表方式实现 可以使用模型自定义实现
     *
     * @param $where
     * @param $field
     *
     * @return \madong\ingenious\interface\model\IProcessTaskHistory|null
     */
    public function getDetail($where = [], $field = ['*']): ?IProcessTaskHistory
    {
        // 定义表前缀
        $prefixes = [
            'pa' => 'pa.',
            'pt' => 'pt.',
            'pd' => 'pd.',
            'pi' => 'pi.',
        ];

        // 获取查询参数
        $where = $this->buildWhere($where, $prefixes);

        // 定义字段
        $field = $this->defineFields($prefixes);

        // 获取表名
        $tableNames = $this->getTableNames();

        // 数据库链接
        $connection = Util::getConnectionConfig();

        // 执行查询
        $query = Db::connect($connection)
            ->table($tableNames['taskhis'])
            ->alias(explode('.', $prefixes['pt'])[0])
            ->field($field)
            ->join([$tableNames['instance'] => 'pi'], 'pt.process_instance_id = pi.id')
            ->join([$tableNames['define'] => 'pd'], 'pi.process_define_id = pd.id')
            ->where($where)
            ->order('pt.create_time', 'desc')
            ->find();

        // 如果没有数据直接返回 null
        if (empty($query)) {
            return null;
        }

        // 准备模型赋值数据
        return $this->mapQueryToModel($query, $connection);
    }

    /**
     * 构建查询条件
     *
     * @param array $where
     * @param array $prefixes
     *
     * @return array
     */
    private function buildWhere(array $where, array $prefixes): array
    {
        $tmap = ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
            ['id', '', '', $prefixes['pt'] . 'id'],
            ['task_name', '', '', $prefixes['pt'] . 'name'],
            ['perform_type', '', '', $prefixes['pt'] . 'perform_type'],
            ['task_display_name', '', '', $prefixes['pt'] . 'display_name'],
            ['operator', '', '', $prefixes['pt'] . 'operator'],
        ]));

        $imap = ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
            ['business_no', '', '', $prefixes['pi'] . 'business_no'],
            ['instance_name', '', '', $prefixes['pi'] . 'name'],
            ['instance_display_name', '', '', $prefixes['pi'] . 'display_name'],
        ]));

        $dmap = ArrayHelper::filterArray(ArrayHelper::paramsFilter($where, [
            ['process_name', '', '', 'name'],
            ['process_display_name', '', '', 'display_name'],
        ]));

        return array_merge($tmap, $imap, $dmap);
    }

    /**
     * 定义字段
     *
     * @param array $prefixes
     *
     * @return string
     */
    private function defineFields(array $prefixes): string
    {
        $actorField = Util::prefixFields(['id', 'process_task_id', 'actor_id', 'create_time', 'create_by', 'update_time', 'update_by'], '');
        $taskField  = Util::prefixFields([
            'id', 'process_instance_id', 'task_name', 'display_name', 'task_type', 'perform_type',
            'task_state', 'operator', 'finish_time', 'expire_time', 'form_key', 'task_parent_id',
            'variable', 'create_time', 'create_by', 'update_time', 'update_by',
        ], $prefixes['pt']);

        $instanceField = Util::prefixFields([
            'id', 'parent_id', 'process_define_id', 'state', 'parent_node_name', 'business_no',
            'operator', 'variable', 'expire_time', 'create_time', 'create_user', 'update_time', 'update_user',
        ], $prefixes['pi']);

        $defineField = Util::prefixFields([
            'id', 'type_id', 'icon', 'name', 'display_name', 'description', 'enabled',
            'is_active', 'content', 'version', 'create_time', 'create_user', 'update_time', 'update_user', 'delete_time',
        ], $prefixes['pd']);

        return Util::mergeFieldStrings([$taskField, $instanceField, $defineField]);
    }

    /**
     * 表名称集合获取
     *
     * @return array
     */
    private function getTableNames(): array
    {
        return [
            'actor'    => ProcessTaskActorHistory::getTableName(),
            'define'   => ProcessDefine::getTableName(),
            'instance' => ProcessInstanceHistory::getTableName(),
            'taskhis'  => ProcessTaskHistory::getTableName(),
        ];
    }

    /**
     * 模型赋值数据
     *
     * @param $query
     * @param $connection
     *
     * @return \madong\ingenious\interface\model\IProcessTaskHistory|null
     */
    private function mapQueryToModel($query, $connection = null): ?IProcessTaskHistory
    {
        $taskData     = Util::convertStringToObject(Util::removePrefix($query, 'pt_', true), 'variable');
        $defineData   = Util::convertStringToObject(Util::removePrefix($query, 'pd_', true), 'content');
        $instanceData = Util::convertStringToObject(Util::removePrefix($query, 'pi_', true), 'variable');

        // 赋值模型
        $taskModel     = $this->getModel();
        $defineModel   = new ProcessDefine();
        $instanceModel = new ProcessInstanceHistory();

        PropertyCopier::copyProperties((object)$taskData, $taskModel);
        PropertyCopier::copyProperties((object)$defineData, $defineModel);
        PropertyCopier::copyProperties((object)$instanceData, $instanceModel);

        // 追加路程定义到流程实例
        $instanceModel->set('define', $defineModel);
        // 流程实例追加到流程任务
        $taskModel->set('instance', $instanceModel);

        $actors = Db::connect($connection)
            ->table($this->getTableNames()['actor'])
            ->where('process_task_id', $taskModel->getData('id'))
            ->select()
            ->toArray();

        // 追加参与人集合
        $taskModel->set('actors', $actors);
        return $taskModel;
    }

}
